package tech.waterism.utils.method;

import cn.hutool.core.util.BooleanUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import tech.waterism.constant.DispatchConstants;
import tech.waterism.document.DPrincipleCustomDocument;
import tech.waterism.dto.*;
import tech.waterism.entity.DStRsvrfsr;
import tech.waterism.entity.FCurveHs;
import tech.waterism.enums.PrincipleLogicEnum;
import tech.waterism.utils.CommonFun;
import tech.waterism.utils.ConvertZV;
import tech.waterism.utils.ForecastDispatchUtils;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

/**
 * 水库调洪
 */
@Component
@Slf4j
public class ResFlood {
    @Autowired
    private ForecastDispatchUtils forecastDispatchUtils;

    public static double waterBalance(double vBeg, double qIn, double qOut, double lSnds) { // 水量平衡法
        double vEnd;
        vEnd = vBeg + (qIn - qOut) * lSnds;
        return vEnd;
    }

    public static double waterBalanceByWaterLevel(double vBeg, double vEnd, double qIn, double lSnds) { // 水量平衡法
        double qOut;
        qOut = (vBeg - vEnd) / lSnds + qIn;
        return qOut;
    }

    /**
     * 按给定的泄流设备开启状态和数量, 推求水库时段末库容 return value — end volume of reservoir,
     * m3. 1. float vBeg — 时段初库容，m3; 2. float qIn — 时段入流量，m3; 3. pLen —
     * 时段长，s; 4. actually spillways[] are used to calculate outflow.
     */
    public static double rangeKutta(List<FCurveHs> curveHs, List<DSpillwaysDto> spillways, double vBeg, double qIn,
                                    double pLen, Integer[][] nUsedGates) { // rangeKutta法
        double k1, k2, k3, k4, vEnd, zEnd, zBeg;
        zBeg = ConvertZV.getZFromV(curveHs, vBeg).getResult();
        k1 = (qIn - ResOutFlow.getOutflow(zBeg, nUsedGates, spillways)) * pLen;// 单位m3
        vEnd = vBeg + k1 / 2;
        k2 = (qIn - ResOutFlow.getOutflow(ConvertZV.getZFromV(curveHs, vEnd).getResult(), nUsedGates, spillways)) *
                pLen;
        vEnd = vBeg + k2 / 2;
        k3 = (qIn - ResOutFlow.getOutflow(ConvertZV.getZFromV(curveHs, vEnd).getResult(), nUsedGates, spillways)) *
                pLen;
        vEnd = vBeg + k3;
        k4 = (qIn - ResOutFlow.getOutflow(ConvertZV.getZFromV(curveHs, vEnd).getResult(), nUsedGates, spillways)) *
                pLen;
        vEnd = vBeg + (k1 + 2 * k2 + 2 * k3 + k4) / 6;
        return vEnd;
    }

    /**
     * 按给定的泄流设备开启状态和数量, 推求水库时段末库容 return value — end volume of reservoir,
     * m3. 1. float vBeg — 时段初库容，m3; 2. float qIn — 时段入流量，m3; 3. pLen —
     * 时段长，s; 4. actually spillways[] are used to calculate outflow.
     */
    public static double rangeKuttaH(List<FCurveHs> curveHs, List<DSpillwaysDto> spillways, double vBeg, double zDown,
                                     double qIn, double pLen, Integer[][] nUsedGates) { // rangeKutta法
        double k1, k2, k3, k4, vEnd, zEnd, zBeg;
        zBeg = ConvertZV.getZFromV(curveHs, vBeg).getResult();
        k1 = (qIn - ResOutFlow.getOutFlowH(zBeg, zBeg - zDown, nUsedGates, spillways)) * pLen;// 单位m3
        vEnd = vBeg + k1 / 2;
        zEnd = ConvertZV.getZFromV(curveHs, vEnd).getResult();
        k2 = (qIn - ResOutFlow.getOutFlowH(zEnd, zEnd - zDown, nUsedGates, spillways)) * pLen;
        vEnd = vBeg + k2 / 2;
        zEnd = ConvertZV.getZFromV(curveHs, vEnd).getResult();
        k3 = (qIn - ResOutFlow.getOutFlowH(zEnd, zEnd - zDown, nUsedGates, spillways)) * pLen;
        vEnd = vBeg + k3;
        zEnd = ConvertZV.getZFromV(curveHs, vEnd).getResult();
        k4 = (qIn - ResOutFlow.getOutFlowH(zEnd, zEnd - zDown, nUsedGates, spillways)) * pLen;
        vEnd = vBeg + (k1 + 2 * k2 + 2 * k3 + k4) / 6;
        return vEnd;
    }

    /**
     * 使用Range-Kutta计算时段末蓄水量
     */
    public static Double rangeKuttaToV(FPIndexDto fpIndex, int timeNum) {
        if (fpIndex.getSpillways() != null && fpIndex.getSpillways().size() > 0) {
            if (fpIndex.getSpillways().get(0).getZqMethods() != null &&
                    fpIndex.getSpillways().get(0).getZqMethods().size() > 0) {
                if (fpIndex.getSpillways().get(0).getZqMethods().get(0).getZqSpillwayHs() != null &&
                        fpIndex.getSpillways().get(0).getZqMethods().get(0).getZqSpillwayHs().size() > 0) {
                    return rangeKuttaH(fpIndex.getCurveHs(), fpIndex.getSpillways(),
                            fpIndex.getData().get(timeNum - 1).getV(),
                            (fpIndex.getData().get(timeNum).getZDown() == null) ? 0.0 : fpIndex.getData().get(timeNum)
                                    .getZDown(), fpIndex.getData().get(timeNum).getQIn(),
                            CommonFun.multiKFromFlowToVolumn(fpIndex.getClen(), fpIndex.getUnt()),
                            fpIndex.getData().get(timeNum).getNUsedGates());
                } else {
                    return rangeKuttaStep(fpIndex.getCurveHs(), fpIndex.getSpillways(),
                            fpIndex.getData().get(timeNum - 1).getV(), fpIndex.getData().get(timeNum).getQIn(),
                            CommonFun.multiKFromFlowToVolumn(fpIndex.getClen(), fpIndex.getUnt()),
                            fpIndex.getData().get(timeNum).getNUsedGates());
                }
            }
        }
        return null;
    }

    /**
     * 初始化闸门数组
     */
    public static void initialGate(FPIndexDto fpIndex, int timeNum) {
        // 初始化gate
        if ((fpIndex.getNotUseQoutMax() != null && fpIndex.getNotUseQoutMax()) || fpIndex.getSpillways() == null ||
                fpIndex.getSpillways().size() <= 0) {
            fpIndex.getData().get(timeNum).setNUsedGates(new Integer[][]{{0}});
        } else {
            Integer[][] nUsedGates = null;
            if (fpIndex.getData().get(timeNum).getNUsedGates() == null ||
                    fpIndex.getData().get(timeNum).getNUsedGates().length <= 0) {
                nUsedGates = new Integer[fpIndex.getSpillways().size()][];
                for (int j = 0; j < fpIndex.getSpillways().size(); j++) {
                    nUsedGates[j] = new Integer[fpIndex.getSpillways().get(j).getNopenstates()];
                    for (int k = 0; k < fpIndex.getSpillways().get(j).getNopenstates(); k++) {
                        nUsedGates[j][k] = 0;
                    }
                }
                fpIndex.getData().get(timeNum).setNUsedGates(nUsedGates);
            }
        }
    }

    public static double rangeKuttaStep(List<FCurveHs> curveHs, List<DSpillwaysDto> spillways, double vBeg, double qIn,
                                        double pLen, Integer[][] nUsedGates) {
        double vEnd = 0;
        double vBegBak = vBeg;
        int n = 5;
        while (true) {
            if (n > 10) {
                break;
            }
            int time = (int) Math.pow(2, n);
            vEnd = 0;
            vBeg = vBegBak;
            double step = (double) 1 / time;
            for (int i = 0; i < time; i++) {
                vEnd = rangeKuttaStep(curveHs, spillways, vBeg, qIn, pLen, nUsedGates, step);
                vBeg = vEnd;
            }
            if (vEnd >= 0) {
                break;
            }
            if (Math.abs(vEnd) <= 0.01) {
                // 小于一个阈值之后就可以停止了
                vEnd = 0;
                break;
            }
            n++;
        }
        if (n > 10) {
            log.error(curveHs.get(0).getStcd() + "插值次数：" + n);
        }
        return vEnd;
    }

    private static double rangeKuttaStep(List<FCurveHs> curveHs, List<DSpillwaysDto> spillways, double vBeg, double qIn,
                                         double pLen, Integer[][] nUsedGates, double step) {
        double k1, k2, k3, k4, vEnd, zEnd, zBeg;
        zBeg = ConvertZV.getZFromV(curveHs, vBeg).getResult();
        k1 = (qIn - ResOutFlow.getOutflow(zBeg, nUsedGates, spillways)) * pLen;// 单位m3
        vEnd = vBeg + k1 * step / 2;
        k2 = (qIn - ResOutFlow.getOutflow(ConvertZV.getZFromV(curveHs, vEnd).getResult(), nUsedGates, spillways)) *
                pLen;
        vEnd = vBeg + k2 * step / 2;
        k3 = (qIn - ResOutFlow.getOutflow(ConvertZV.getZFromV(curveHs, vEnd).getResult(), nUsedGates, spillways)) *
                pLen;
        vEnd = vBeg + k3 * step;
        k4 = (qIn - ResOutFlow.getOutflow(ConvertZV.getZFromV(curveHs, vEnd).getResult(), nUsedGates, spillways)) *
                pLen;
        vEnd = vBeg + step * (k1 + 2 * k2 + 2 * k3 + k4) / 6;
        return vEnd;
    }

    /**
     * 根据调洪规则进行每个时段的调洪计算
     */
    private static boolean resRoutingOnePeriodByPrinciple(FPIndexDto fpIndex, int timeNum, double zxunxian) {
        /**
         * 1个时段调洪: 按给定泄流方式res.outflowWay[i]
         */
        if (fpIndex.getData().get(timeNum).getFlowWay() == DispatchConstants.FLOW_WAY_BALANCE_BY_OUTFLOW) { // 水量平衡
            fpIndex.getData().get(timeNum).setV(waterBalance(fpIndex.getData().get(timeNum - 1).getV(),
                    fpIndex.getData().get(timeNum).getQIn(), fpIndex.getData().get(timeNum).getQOut(),
                    CommonFun.multiKFromFlowToVolumn(fpIndex.getClen(), fpIndex.getUnt())));
            CurveZVDto zvDto = ConvertZV.getVFromZ(fpIndex.getCurveHs(), zxunxian);
            double vxunxian = zvDto.getResult() + 1000;
            ConvertZV.setErrMsgCurveHs(fpIndex, zvDto.getMsg());
            if (fpIndex.getData().get(timeNum).getV() < vxunxian) {
                fpIndex.getData().get(timeNum).setQOut(fpIndex.getData().get(timeNum).getQIn() -
                        (vxunxian - fpIndex.getData().get(timeNum - 1).getV()) /
                                CommonFun.multiKFromFlowToVolumn(fpIndex.getClen(), fpIndex.getUnt()));
                if (fpIndex.getData().get(timeNum).getQOut() < 0)
                    fpIndex.getData().get(timeNum).setQOut(0.0);
            }
            fpIndex.getData().get(timeNum).setV(waterBalance(fpIndex.getData().get(timeNum - 1).getV(),
                    fpIndex.getData().get(timeNum).getQIn(), fpIndex.getData().get(timeNum).getQOut(),
                    CommonFun.multiKFromFlowToVolumn(fpIndex.getClen(), fpIndex.getUnt())));
            CurveZVDto zvDto1 = ConvertZV.getZFromV(fpIndex.getCurveHs(), fpIndex.getData().get(timeNum).getV());
            fpIndex.getData().get(timeNum).setRz(zvDto1.getResult());
            ConvertZV.setErrMsgCurveHs(fpIndex, zvDto1.getMsg());
        } else if (fpIndex.getData().get(timeNum).getFlowWay() ==
                DispatchConstants.FLOW_WAY_GATE_BY_RUNGEKUTTA) { // Range-Kutta
            // todo 调度规则不常用，暂时不做切换插值方式
            fpIndex.getData().get(timeNum).setV(rangeKuttaToV(fpIndex, timeNum));
            CurveZVDto zvDto1 = ConvertZV.getVFromZ(fpIndex.getCurveHs(), zxunxian);
            double vxunxian = zvDto1.getResult() + 1000;
            ConvertZV.setErrMsgCurveHs(fpIndex, zvDto1.getMsg());
            if (fpIndex.getData().get(timeNum).getV() != null) {
                if (fpIndex.getData().get(timeNum).getV() < vxunxian) {
                    fpIndex.getData().get(timeNum).setQOut(fpIndex.getData().get(timeNum).getQIn() -
                            (vxunxian - fpIndex.getData().get(timeNum - 1).getV()) /
                                    CommonFun.multiKFromFlowToVolumn(fpIndex.getClen(), fpIndex.getUnt()));
                    if (fpIndex.getData().get(timeNum).getQOut() < 0)
                        fpIndex.getData().get(timeNum).setQOut(0.0);
                    fpIndex.getData().get(timeNum).setV(waterBalance(fpIndex.getData().get(timeNum - 1).getV(),
                            fpIndex.getData().get(timeNum).getQIn(), fpIndex.getData().get(timeNum).getQOut(),
                            CommonFun.multiKFromFlowToVolumn(fpIndex.getClen(), fpIndex.getUnt())));
                    CurveZVDto zvDto = ConvertZV.getZFromV(fpIndex.getCurveHs(), fpIndex.getData().get(timeNum).getV());
                    fpIndex.getData().get(timeNum).setRz(zvDto.getResult());
                    ConvertZV.setErrMsgCurveHs(fpIndex, zvDto.getMsg());
                    fpIndex.getData().get(timeNum).setFlowWay(DispatchConstants.FLOW_WAY_BALANCE_BY_OUTFLOW);
                    Integer[][] nUsedGates = null;
                    if (fpIndex.getData().get(timeNum).getNUsedGates() == null ||
                            fpIndex.getData().get(timeNum).getNUsedGates().length <= 0) {
                        nUsedGates = new Integer[fpIndex.getSpillways().size()][];
                        for (int j = 0; j < fpIndex.getSpillways().size(); j++) {
                            nUsedGates[j] = new Integer[fpIndex.getSpillways().get(j).getNopenstates()];
                            for (int k = 0; k < fpIndex.getSpillways().get(j).getNopenstates(); k++) {
                                nUsedGates[j][k] = 0;
                            }
                        }
                        fpIndex.getData().get(timeNum).setNUsedGates(nUsedGates);
                    }
                } else {
                    CurveZVDto zvDto = ConvertZV.getZFromV(fpIndex.getCurveHs(), fpIndex.getData().get(timeNum).getV());
                    fpIndex.getData().get(timeNum).setRz(zvDto.getResult());
                    ConvertZV.setErrMsgCurveHs(fpIndex, zvDto.getMsg());
                    fpIndex.getData().get(timeNum).setQOut(fpIndex.getData().get(timeNum).getQIn() -
                            (fpIndex.getData().get(timeNum).getV() - fpIndex.getData().get(timeNum - 1).getV()) /
                                    CommonFun.multiKFromFlowToVolumn(fpIndex.getClen(), fpIndex.getUnt()));
                }
            }
        }
        if (fpIndex.getData().get(timeNum).getQOut() == null || fpIndex.getData().get(timeNum).getQOut() < 0)
            fpIndex.getData().get(timeNum).setQOut(0.0);
        return true;
    }

    /**
     * 获取汛限水位
     */
    public static double getZNSummer(List<DStRsvrfsr> stRsvrfsr, Date date) {
        double zxunxian = -1;
        SimpleDateFormat sf = new SimpleDateFormat("MMdd");
        String dtStr = sf.format(date);
        int bg, ed, dt;
        for (DStRsvrfsr dStRsvrfsr : stRsvrfsr) {
            if (dStRsvrfsr.getBgmd() != null && dStRsvrfsr.getEdmd() != null) {
                bg = Integer.parseInt(dStRsvrfsr.getBgmd());
                ed = Integer.parseInt(dStRsvrfsr.getEdmd());
                dt = Integer.parseInt(dtStr);
                if (dt >= bg && dt <= ed) {
                    zxunxian = dStRsvrfsr.getFsltdz();
                    break;
                }
            }
        }
        return zxunxian;
    }

    /**
     * 调度规则方案
     */
    public DispatchTempDataDto makePlanByGivenInflow(DispatchTempDataDto dispatchTempData, int indexObject,
                                                     List<TopoObjectDto> topoObjects, PrincipleData principleData, int timeNum, int timeNumOperate) {
        // 第0个时段不用计算, 只需将库容计算出来即可
        if (timeNum == timeNumOperate) {
            CurveZVDto zvDto = ConvertZV.getVFromZ(topoObjects.get(indexObject).getFpIndex().getCurveHs(),
                    topoObjects.get(indexObject).getFpIndex().getData().get(timeNum).getRz());
            topoObjects.get(indexObject).getFpIndex().getData().get(timeNum).setV(zvDto.getResult());
            topoObjects.get(indexObject).getFpIndex().setErrMsgCurveHs(zvDto.getMsg());
            topoObjects.get(indexObject).getFpIndex().getData().get(timeNum)
                    .setFlowWay(DispatchConstants.FLOW_WAY_BALANCE_BY_OUTFLOW);
        } else {
            dispatchTempData = makePlanByPrinciple(dispatchTempData, indexObject, topoObjects, principleData, timeNum,
                    timeNumOperate);
        }
        return dispatchTempData;
    }

    /**
     * 自定义调度规则
     */
    @SneakyThrows
    public DispatchTempDataDto makePlanByCustomPrinciple(DispatchTempDataDto dispatchTempData, FPIndexDto fpIndex,
                                                         int timeNum, int timeNumOperate) {
        // 第0个时段不用计算, 只需将库容计算出来即可
        if (timeNum == timeNumOperate) {
            CurveZVDto zvDto = ConvertZV.getVFromZ(fpIndex.getCurveHs(), fpIndex.getData().get(timeNum).getRz());
            fpIndex.getData().get(timeNum).setV(zvDto.getResult());
            fpIndex.setErrMsgCurveHs(zvDto.getMsg());
            fpIndex.getData().get(timeNum).setFlowWay(DispatchConstants.FLOW_WAY_BALANCE_BY_OUTFLOW);
        } else {
            dispatchTempData = makePlanByCustomPrincipleOther(dispatchTempData, fpIndex, timeNum, timeNumOperate);
        }
        return dispatchTempData;
    }

    @SneakyThrows
    public DispatchTempDataDto makePlanByCustomPrincipleOther(DispatchTempDataDto dispatchTempData, FPIndexDto fpIndex,
                                                              int timeNum, int timeNumOperate) {
        ForecastDataDto datum = fpIndex.getData().get(timeNum);
        // 如果上一条数据有做赋值，这条数据先给出流赋值
        ForecastDataDto preDatum = fpIndex.getData().get(timeNum - 1);
        if (Boolean.TRUE.equals(preDatum.getSetFlag())) {
            datum.setSetFlag(true);
            datum.setQOut(preDatum.getQOut());
        }
        DPrincipleCustomDocument principleCustom = fpIndex.getPrincipleCustom();
        if (principleCustom == null) {
            dispatchTempData.setRecordNo(null);
            return dispatchTempData;
        }
        LogFieldDto logFieldDto = new LogFieldDto().setStcd(fpIndex.getStcd()).setStnm(fpIndex.getStnm());
        boolean calFlg = false;
        for (PrincipleRecord record : fpIndex.getPrincipleCustom().getRecords()) {
            if (calFlg) {
                break;
            }
            if (StringUtils.isNotBlank(record.getParentRecordNo()) &&
                    !record.getParentRecordNo().equals(dispatchTempData.getRecordNo())) {
                continue;
            }
            Date curDate = datum.getDt();
            if (BooleanUtil.isTrue(record.getWithRange())) {
                if (!forecastDispatchUtils.inCalTime(record, curDate)) {
                    continue;
                }
            }
            if (Boolean.TRUE.equals(record.getWithCondition())) {
                if (record.getLogic() == null) {
                    record.setLogic(PrincipleLogicEnum.AND);
                }
                switch (record.getLogic()) {
                    case OR:
                        for (PrincipleCondition condition : record.getConditions()) {
                            if (forecastDispatchUtils.getCondition(condition, logFieldDto, fpIndex, timeNum,
                                    timeNumOperate)) {
                                forecastDispatchUtils.setValue(record, logFieldDto, fpIndex, timeNum, timeNumOperate);
                                calFlg = true;
                                dispatchTempData.setRecordNo(record.getRecordNo());
                                if (record.getControls().stream().anyMatch(i -> i.getLogic().equals(LogicEnum.EQ))) {
                                    datum.setSetFlag(true);
                                }
                                break;
                            }
                        }
                        break;
                    case AND:
                        boolean flag = true;
                        for (PrincipleCondition condition : record.getConditions()) {
                            flag = flag && forecastDispatchUtils.getCondition(condition, logFieldDto, fpIndex, timeNum,
                                    timeNumOperate);
                        }
                        if (flag) {
                            forecastDispatchUtils.setValue(record, logFieldDto, fpIndex, timeNum, timeNumOperate);
                            calFlg = true;
                            dispatchTempData.setRecordNo(record.getRecordNo());
                            if (record.getControls().stream().anyMatch(i -> i.getLogic().equals(LogicEnum.EQ))) {
                                datum.setSetFlag(true);
                            }
                        }
                        break;
                    default:
                        throw new Exception("Unexpected value: " + record.getLogic().getName());
                }
            } else {
                forecastDispatchUtils.setValue(record, logFieldDto, fpIndex, timeNum, timeNumOperate);
                calFlg = true;
                dispatchTempData.setRecordNo(record.getRecordNo());
                if (record.getControls().stream().anyMatch(i -> i.getLogic().equals(LogicEnum.EQ))) {
                    datum.setSetFlag(true);
                }
            }
        }
        if (!calFlg) {
            // 清空前序计算条件
            dispatchTempData.setRecordNo(null);
            // 不计算的时候，进行平衡计算
            forecastDispatchUtils.normalWaterbalance(fpIndex, datum, timeNum);
        }
        return dispatchTempData;
    }

    /**
     * 按调度规则检查方案
     */
    public DispatchTempDataDto checkPlanByGivenInflow(DispatchTempDataDto dispatchTempData, int indexObject,
                                                      List<TopoObjectDto> topoObjects, PrincipleData principleData, int timeNum, int timeNumOperate) {
        // 第0个时段不用计算, 只需将库容计算出来即可
        if (timeNum > timeNumOperate) {
            dispatchTempData = checkPlanByPrinciple(dispatchTempData, indexObject, topoObjects, principleData, timeNum,
                    timeNumOperate);
        }
        return dispatchTempData;
    }

    /**
     * 天然流量方案——出流=入流
     */
    public void makePlanByGivenInflowToOutflow(FPIndexDto fpIndex, int timeNum, int timeNumOperate) {
        if (timeNum > timeNumOperate) {
            fpIndex.getData().get(timeNum).setQOut(fpIndex.getData().get(timeNum).getQIn());
            fpIndex.getData().get(timeNum).setFlowWay(DispatchConstants.FLOW_WAY_BALANCE_BY_OUTFLOW);
        }
        if (fpIndex.getCurveHs() == null || fpIndex.getCurveHs().size() <= 1)
            return;
        makePlanByGivenOutWays(fpIndex, timeNum, timeNumOperate);
    }

    /**
     * 按当前出流方案——若无实测出流过程，则将初始出流赋值给后续出流过程
     */
    public void makePlanByGivenOutflowBegToOutflow(FPIndexDto fpIndex, int timeNum, int timeNumOperate) {
        if (timeNum > timeNumOperate) {
            if (fpIndex.getData().get(timeNum).getQOutIsExist() == null ||
                    !fpIndex.getData().get(timeNum).getQOutIsExist()) {
                fpIndex.getData().get(timeNum).setQOut(fpIndex.getData().get(timeNum - 1).getQOut());
                fpIndex.getData().get(timeNum).setQOutIsExist(true);
            }
            fpIndex.getData().get(timeNum).setFlowWay(DispatchConstants.FLOW_WAY_BALANCE_BY_OUTFLOW);
        }
        if (fpIndex.getCurveHs() == null || fpIndex.getCurveHs().size() <= 1)
            return;
        makePlanByGivenOutWays(fpIndex, timeNum, timeNumOperate);
    }

    /**
     * 按给定水位方案
     */
    public void makePlanByGivenWaterLevelToOutflow(FPIndexDto fpIndex, int timeNum, int timeNumOperate) {
        if (timeNum > timeNumOperate) {
            fpIndex.getData().get(timeNum).setFlowWay(DispatchConstants.FLOW_WAY_BALANCE_BY_WATERLEVEL);
        }
        if (fpIndex.getCurveHs() == null || fpIndex.getCurveHs().size() <= 1)
            return;
        makePlanByGivenOutWays(fpIndex, timeNum, timeNumOperate);
    }

    /**
     * 按给定初值方案
     */
    public void makePlanByGivenInitialValueToOutflow(FPIndexDto fpIndex, int timeNum, int timeNumOperate) {
        if (fpIndex.getCurveHs() == null || fpIndex.getCurveHs().size() <= 1)
            return;
        makePlanByGivenOutWays(fpIndex, timeNum, timeNumOperate);
    }

    /**
     * 按给定泄流方式进行调洪计算
     *
     * @return boolean
     */
    public boolean makePlanByGivenOutWays(FPIndexDto fpIndex, int timeNum, int timeNumOperate) {
        if (timeNum == timeNumOperate) {
            CurveZVDto zvDto = ConvertZV.getVFromZ(fpIndex.getCurveHs(), fpIndex.getData().get(timeNum).getRz());
            fpIndex.getData().get(timeNum).setV(zvDto.getResult());
            fpIndex.setErrMsgCurveHs(zvDto.getMsg());
            fpIndex.getData().get(timeNum).setFlowWay(DispatchConstants.FLOW_WAY_BALANCE_BY_OUTFLOW);
        } else {
            if (fpIndex.getData().get(timeNum).getFlowWay() ==
                    DispatchConstants.FLOW_WAY_BALANCE_BY_WATERLEVEL) { // 按给定水位调算
                // 初始化gate
                initialGate(fpIndex, timeNum);
            } else if (fpIndex.getData().get(timeNum).getFlowWay() ==
                    DispatchConstants.FLOW_WAY_BALANCE_BY_OUTFLOW) { // 按给定出流调算
                // 初始化gate
                initialGate(fpIndex, timeNum);
            } else if (fpIndex.getData().get(timeNum).getFlowWay() ==
                    DispatchConstants.FLOW_WAY_GATE_BY_RUNGEKUTTA) { // 按给定闸门开度调算
                String mannerNote = "";
                for (int j = 0; j < fpIndex.getSpillways().size(); j++) {
                    // 在以后的编程过程中如果有对象（包括数组）的句柄赋值，一定要小心；
                    for (int k = 0; k < fpIndex.getSpillways().get(j).getNopenstates(); k++) {
                        if (fpIndex.getData().get(timeNum).getNUsedGates()[j][k] > 0) {
                            mannerNote = mannerNote + fpIndex.getSpillways().get(j).getShortname() + " " +
                                    fpIndex.getSpillways().get(j).getZqMethods().get(k).getStatename() + "-" +
                                    fpIndex.getData().get(timeNum).getNUsedGates()[j][k] + ";";
                        }
                    } // end for
                } // end for
                fpIndex.getData().get(timeNum).setGateNote(mannerNote);
            } else if (fpIndex.getData().get(timeNum).getFlowWay() ==
                    DispatchConstants.FLOW_WAY_GIVEN_BY_NATUREINFLOW) { // 按天然出流调算
                fpIndex.getData().get(timeNum).setQOut(fpIndex.getData().get(timeNum).getQIn()); // 按出流等于入流
                fpIndex.getData().get(timeNum).setFlowWay(DispatchConstants.FLOW_WAY_BALANCE_BY_OUTFLOW);
                // 初始化gate
                initialGate(fpIndex, timeNum);
            } // end if
            resRoutingOnePeriod(fpIndex, timeNum);
        }
        return true;
    }

    /***
     * 按规划的洪水调度原则进行调洪计算, 单时段
     *
     * @param principleData
     *            调度规则
     * @param timeNum
     *            时段
     * @return
     */
    private DispatchTempDataDto makePlanByPrinciple(DispatchTempDataDto dispatchTempData, int indexObject,
                                                    List<TopoObjectDto> topoObjects, PrincipleData principleData, int timeNum, int timeNumOperate) {
        // 获取汛限水位, 在每个时段调洪计算时使用
        double zxunxian = getZNSummer(topoObjects.get(indexObject).getFpIndex().getFloodCharacter().getStRsvrfsrBList(),
                topoObjects.get(indexObject).getFpIndex().getData().get(timeNum).getDt());
        dispatchTempData = principleData.getOutFlowWayByPrinciple(dispatchTempData, indexObject, topoObjects, timeNum,
                timeNumOperate);
        int[] iGradeData = dispatchTempData.getIGradeData();
        topoObjects.get(indexObject).getFpIndex().getData().get(timeNum).setFlowWay(iGradeData[0]);
        if (topoObjects.get(indexObject).getFpIndex().getData().get(timeNum).getFlowWay() ==
                DispatchConstants.FLOW_WAY_BALANCE_BY_OUTFLOW) { // 常量
            initialGate(topoObjects.get(indexObject).getFpIndex(), timeNum);
        } else if (topoObjects.get(indexObject).getFpIndex().getData().get(timeNum).getFlowWay() ==
                DispatchConstants.FLOW_WAY_GATE_BY_RUNGEKUTTA) { // 交互式
            String mannerNote = "";
            for (int j = 0; j < topoObjects.get(indexObject).getFpIndex().getSpillways().size(); j++) {
                // 在以后的编程过程中如果有对象（包括数组）的句柄赋值，一定要小心；
                for (int k = 0; k < topoObjects.get(indexObject).getFpIndex().getSpillways().get(j).getNopenstates();
                     k++) {
                    if (topoObjects.get(indexObject).getFpIndex().getData().get(timeNum).getNUsedGates()[j][k] > 0) {
                        mannerNote = mannerNote +
                                topoObjects.get(indexObject).getFpIndex().getSpillways().get(j).getShortname() + " " +
                                topoObjects.get(indexObject).getFpIndex().getSpillways().get(j).getZqMethods().get(k)
                                        .getStatename() + "-" +
                                topoObjects.get(indexObject).getFpIndex().getData().get(timeNum).getNUsedGates()[j][k] +
                                ";";
                    }
                }
            }
            topoObjects.get(indexObject).getFpIndex().getData().get(timeNum).setGateNote(mannerNote);
        }
        resRoutingOnePeriodByPrinciple(topoObjects.get(indexObject).getFpIndex(), timeNum, zxunxian);
        return dispatchTempData;
    }

    private DispatchTempDataDto checkPlanByPrinciple(DispatchTempDataDto dispatchTempData, int indexObject,
                                                     List<TopoObjectDto> topoObjects, PrincipleData principleData, int timeNum, int timeNumOperate) {
        FPIndexDto fpIndex = topoObjects.get(indexObject).getFpIndex();
        ForecastDataDto curData = fpIndex.getData().get(timeNum);
        Integer clen = fpIndex.getClen();
        String unt = fpIndex.getUnt();
        // 获取汛限水位, 在每个时段调洪计算时使用
        if (fpIndex.getFloodCharacter() == null || fpIndex.getFloodCharacter().getStRsvrfsrBList() == null) {
            return dispatchTempData;
        }
        double zxunxian = getZNSummer(fpIndex.getFloodCharacter().getStRsvrfsrBList(), curData.getDt());
        dispatchTempData = principleData.checkOutFlowWayByPrinciple(dispatchTempData, indexObject, topoObjects, timeNum,
                timeNumOperate);
        if (dispatchTempData.getDoNotCheck() != null && dispatchTempData.getDoNotCheck()) {
            return dispatchTempData;
        }
        int[] checkGradeData = dispatchTempData.getCheckGradeData();
        if (checkGradeData[0] == 1) { // 清0
            if (fpIndex.getSpillways() == null || fpIndex.getSpillways().size() <= 0) {
                curData.setNUsedGatesPrinciple(new Integer[][]{{0}});
            } else {
                double qOut = ResOutFlow.checkOutflow(fpIndex.getData().get(timeNum - 1).getRz(),
                        curData.getQOutMaxPrinciple(), fpIndex.getSpillways());
                curData.setQOutMaxPrinciple(qOut);
                Integer[][] nUsedGates = null;
                // 初始化启闭状态
                if (curData.getNUsedGatesPrinciple() == null || curData.getNUsedGatesPrinciple().length <= 0) {
                    nUsedGates = new Integer[fpIndex.getSpillways().size()][];
                    for (int j = 0; j < fpIndex.getSpillways().size(); j++) {
                        nUsedGates[j] = new Integer[fpIndex.getSpillways().get(j).getNopenstates()];
                        for (int k = 0; k < fpIndex.getSpillways().get(j).getNopenstates(); k++) {
                            nUsedGates[j][k] = 0;
                        }
                    }
                    curData.setNUsedGatesPrinciple(nUsedGates);
                }
            }
        } else if (checkGradeData[0] == 2) { // 交互式
            Integer[][] nUsedGates = null;
            // 初始化启闭状态
            if (curData.getNUsedGatesPrinciple() == null || curData.getNUsedGatesPrinciple().length <= 0) {
                nUsedGates = new Integer[fpIndex.getSpillways().size()][];
                for (int j = 0; j < fpIndex.getSpillways().size(); j++) {
                    nUsedGates[j] = new Integer[fpIndex.getSpillways().get(j).getNopenstates()];
                    for (int k = 0; k < fpIndex.getSpillways().get(j).getNopenstates(); k++) {
                        nUsedGates[j][k] = 0;
                    }
                }
                curData.setNUsedGatesPrinciple(nUsedGates);
            }
            double v = fpIndex.getData().get(timeNum - 1).getV();
            if (fpIndex.getSpillways() != null && fpIndex.getSpillways().size() > 0) {
                if (fpIndex.getSpillways().get(0).getZqMethods() != null &&
                        fpIndex.getSpillways().get(0).getZqMethods().size() > 0) {
                    if (fpIndex.getSpillways().get(0).getZqMethods().get(0).getZqSpillwayHs() != null &&
                            fpIndex.getSpillways().get(0).getZqMethods().get(0).getZqSpillwayHs().size() > 0) {
                        v = rangeKuttaH(fpIndex.getCurveHs(), fpIndex.getSpillways(),
                                fpIndex.getData().get(timeNum - 1).getV(),
                                (curData.getZDown() == null) ? 0.0 : curData.getZDown(), curData.getQIn(),
                                CommonFun.multiKFromFlowToVolumn(clen, unt), curData.getNUsedGatesPrinciple());
                    } else {
                        v = rangeKutta(fpIndex.getCurveHs(), fpIndex.getSpillways(),
                                fpIndex.getData().get(timeNum - 1).getV(), curData.getQIn(),
                                CommonFun.multiKFromFlowToVolumn(clen, unt), curData.getNUsedGatesPrinciple());
                    }
                }
            }
            CurveZVDto zvDto = ConvertZV.getVFromZ(fpIndex.getCurveHs(), zxunxian);
            double vxunxian = zvDto.getResult() + 1000;
            fpIndex.setErrMsgCurveHs(zvDto.getMsg());
            if (v < vxunxian) {
                curData.setQOutMaxPrinciple(curData.getQIn() - (vxunxian - fpIndex.getData().get(timeNum - 1).getV()) /
                        CommonFun.multiKFromFlowToVolumn(clen, unt));
                if (curData.getQOutMaxPrinciple() < 0)
                    curData.setQOutMaxPrinciple(0.0);
            } else {
                curData.setQOutMaxPrinciple(curData.getQIn() -
                        (v - fpIndex.getData().get(timeNum - 1).getV()) / CommonFun.multiKFromFlowToVolumn(clen, unt));
            }
        }
        return dispatchTempData;
    }

    /**
     * 出流校验
     */
    private double checkOutFlow(FPIndexDto fpIndex, int timeNum) {
        if (Boolean.TRUE.equals(fpIndex.getNotUseQoutMax())) {
            return Double.MAX_VALUE;
        }
        double qOut = fpIndex.getData().get(timeNum).getQOut();
        double z = fpIndex.getData().get(timeNum - 1).getRz();
        if (fpIndex.getSpillways() != null && !fpIndex.getSpillways().isEmpty()) {
            if (fpIndex.getSpillways().get(0).getZqMethods() != null &&
                    !fpIndex.getSpillways().get(0).getZqMethods().isEmpty()) {
                if (fpIndex.getSpillways().get(0).getZqMethods().get(0).getZqSpillwayHs() != null &&
                        !fpIndex.getSpillways().get(0).getZqMethods().get(0).getZqSpillwayHs().isEmpty()) {
                    double zDown =
                            (fpIndex.getData().get(timeNum).getZDown() == null) ? 0.0 : fpIndex.getData().get(timeNum)
                                    .getZDown();
                    double h = z - zDown;
                    if (h > 0) {
                        qOut = ResOutFlow.checkOutflowH(z, h, qOut, fpIndex.getSpillways());
                    } else {
                        qOut = 0;
                    }
                } else {
                    // qOut = ResOutFlow.checkOutflow(z, qOut, fpIndex.getSpillways());
                    // change logic
                    qOut = ForecastDispatchUtils.getQoutMaxByGateOpenAll(fpIndex, fpIndex.getData().get(timeNum),
                            timeNum);
                }
            }
        }
        return qOut;
    }

    /**
     * 根据调洪规则进行每个时段的调洪计算
     */
    private boolean resRoutingOnePeriod(FPIndexDto fpIndex, int timeNum) {
        ForecastDataDto data = fpIndex.getData().get(timeNum);
        ForecastDataDto foreData = fpIndex.getData().get(timeNum - 1);
        /**
         * 1个时段调洪: 按给定泄流方式res.outflowWay[i]
         */
        if (data.getFlowWay() == DispatchConstants.FLOW_WAY_BALANCE_BY_WATERLEVEL) {
            // 按给定水位调算
            if (data.getRz() != null) {
                CurveZVDto zvDto = ConvertZV.getVFromZ(fpIndex.getCurveHs(), data.getRz());
                data.setV(zvDto.getResult());
                ConvertZV.setErrMsgCurveHs(fpIndex, zvDto.getMsg());
                data.setQOut(waterBalanceByWaterLevel(foreData.getV(), data.getV(), data.getQIn(),
                        CommonFun.multiKFromFlowToVolumn(fpIndex.getClen(), fpIndex.getUnt())));
                data.setQOutIsExist(true);
                double qOutMax = checkOutFlow(fpIndex, timeNum);
                // 是否超过水库下泄能力上限
                if (data.getQOut() > qOutMax) {
                    data.setQOut(qOutMax);
                    // 重新计算时段末水位
                    data.setV(waterBalance(foreData.getV(), data.getQIn(), data.getQOut(),
                            CommonFun.multiKFromFlowToVolumn(fpIndex.getClen(), fpIndex.getUnt())));
                    zvDto = ConvertZV.getZFromV(fpIndex.getCurveHs(), data.getV());
                    data.setRz(zvDto.getResult());
                    ConvertZV.setErrMsgCurveHs(fpIndex, zvDto.getMsg());
                }
            }
        } else if (data.getFlowWay() == DispatchConstants.FLOW_WAY_BALANCE_BY_OUTFLOW) {
            // 按给定出流调算
            if (data.getQOut() != null) {
                double qOutMax = checkOutFlow(fpIndex, timeNum);
                if (data.getQOut() > qOutMax) {
                    data.setQOut(qOutMax);
                }
                double v;
                if (DispatchConstants.DISPATCH_OUTFLOWTYPE_SEMI_GRAPHICAL.equals(fpIndex.getOutFlowType())) {
                    v = waterBalance(foreData.getV(), (data.getQIn() + foreData.getQIn()) / 2,
                            (data.getQOut() + foreData.getQOut()) / 2,
                            CommonFun.multiKFromFlowToVolumn(fpIndex.getClen(), fpIndex.getUnt()));
                } else {
                    v = waterBalance(foreData.getV(), data.getQIn(), data.getQOut(),
                            CommonFun.multiKFromFlowToVolumn(fpIndex.getClen(), fpIndex.getUnt()));
                }
                data.setV(v);
                if (data.getV() < 0) {
                    data.setV(0d);
                    data.setQOut(waterBalanceByWaterLevel(foreData.getV(), data.getV(), data.getQIn(),
                            CommonFun.multiKFromFlowToVolumn(fpIndex.getClen(), fpIndex.getUnt())));
                }
                CurveZVDto zvDto = ConvertZV.getZFromV(fpIndex.getCurveHs(), data.getV());
                data.setRz(zvDto.getResult());
                ConvertZV.setErrMsgCurveHs(fpIndex, zvDto.getMsg());
            }
        } else if (data.getFlowWay() == DispatchConstants.FLOW_WAY_GATE_BY_RUNGEKUTTA) {
            // 按给定闸门开度调算
            if (DispatchConstants.DISPATCH_OUTFLOWTYPE_SEMI_GRAPHICAL.equals(fpIndex.getOutFlowType())) {
                double qout = ForecastDispatchUtils.getQoutBySemiGraphical(fpIndex, timeNum, data.getNUsedGates());
                data.setQOut(qout);
                data.setV(waterBalance(fpIndex.getData().get(timeNum - 1).getV(), data.getQIn(), data.getQOut(),
                        CommonFun.multiKFromFlowToVolumn(fpIndex.getClen(), fpIndex.getUnt())));
                if (data.getV() < 0) {
                    data.setV(0d);
                    data.setQOut(waterBalanceByWaterLevel(foreData.getV(), data.getV(), data.getQIn(),
                            CommonFun.multiKFromFlowToVolumn(fpIndex.getClen(), fpIndex.getUnt())));
                }
                CurveZVDto zvDto = ConvertZV.getZFromV(fpIndex.getCurveHs(), data.getV());
                data.setRz(zvDto.getResult());
                ConvertZV.setErrMsgCurveHs(fpIndex, zvDto.getMsg());
            } else {
                data.setV(rangeKuttaToV(fpIndex, timeNum));
                if (data.getV() != null) {
                    CurveZVDto zvDto = ConvertZV.getZFromV(fpIndex.getCurveHs(), data.getV());
                    data.setRz(zvDto.getResult());
                    ConvertZV.setErrMsgCurveHs(fpIndex, zvDto.getMsg());
                    data.setQOut(waterBalanceByWaterLevel(foreData.getV(), data.getV(), data.getQIn(),
                            CommonFun.multiKFromFlowToVolumn(fpIndex.getClen(), fpIndex.getUnt())));
                }
            }
        }
        if (data.getQOut() != null && data.getQOut() < 0)
            data.setQOut(0.0);
        return true;
    }
}
